#include	"../utils/utils.h"
#include	"../utils/datetime.h"
#include	"../utils/crypto.h"

#include	<algorithm>
#include	<random>

extern "C" {
#include	<luajit.h>
#include	<lualib.h>
#include	<lauxlib.h>
}

namespace {

    static int Random(lua_State * lua) {
        std::random_device r;
        double seed = r() * 1.0 / (r.max() - r.min());

        int top = lua_gettop(lua);
        if (top == 0) {
            lua_pushnumber(lua, (lua_Number)seed);
        } else if (top == 1) {
            lua_pushnumber(lua, (lua_Number)(seed * lua_tonumber(lua, 1)));
        } else {
            double min = lua_tonumber(lua, 1);
            double max = lua_tonumber(lua, 2);
            lua_pushnumber(lua, (lua_Number)(seed * (max - min) + min));
        }

        return 1;
    }

    static int GetTick(lua_State * lua) {
        lua_pushnumber(lua, (lua_Number)Tick());
        return 1;
    }

    static int Inherit(lua_State * lua) {
        luaL_checktype(lua, 1, LUA_TTABLE);
        
        lua_newtable(lua);

        lua_pushvalue(lua, 1);
        lua_setfield(lua, -2, "super");
        
        lua_newtable(lua);
        lua_pushcfunction(lua, [](lua_State * l) {
            lua_getfield(l, 1, "super");
            lua_pushvalue(l, 2);
            lua_rawget(l, -2);
            return 1;
        });
        lua_setfield(lua, -2, "__index");
        lua_setmetatable(lua, -2);

        return 1;
    }

    static int UUID(lua_State * lua) {
        lua_pushstring(lua, CreateId().c_str());
        return 1;
    }

    static int CalcMD5(lua_State * lua) {
        size_t len = 0;
        const char * mem = lua_tolstring(lua, 1, &len);
        lua_pushstring(lua, MD5(mem, len).c_str());
        return 1;
    }

    static int B64Encode(lua_State * lua) {
        size_t len = 0;
        const char * mem = lua_tolstring(lua, 1, &len);
        lua_pushstring(lua, Base64Encode(mem, len).c_str());
        return 1;
    }

    static int B64Decode(lua_State * lua) {
        size_t len = 0;
        const char * mem = lua_tolstring(lua, 1, &len);
        std::string org = Base64Decode(mem, len);
        lua_pushlstring(lua, org.data(), org.size());
        return 1;
    }

    static int CalcCRC32(lua_State * lua) {
        size_t len = 0;
        const char * mem = lua_tolstring(lua, 1, &len);
        lua_pushinteger(lua, (lua_Integer)CRC32(mem, len, 0));
        return 1;
    }

    static int CalcHash(lua_State * lua) {
        size_t len = 0;
        const char * mem = lua_tolstring(lua, 1, &len);
        lua_pushinteger(lua, (lua_Integer)BKDRHash(mem, len));
        return 1;
    }
}

void RegisterApi_Utils(lua_State * lua) {
    lua_pushcfunction(lua, &CalcCRC32);
	lua_setglobal(lua, "crc32");

    lua_pushcfunction(lua, &CalcHash);
	lua_setglobal(lua, "hash");

    lua_pushcfunction(lua, &CalcMD5);
	lua_setglobal(lua, "md5");

	lua_pushcfunction(lua, &Random);
	lua_setglobal(lua, "random");

	lua_pushcfunction(lua, &GetTick);
	lua_setglobal(lua, "tick");

	lua_pushcfunction(lua, &Inherit);
	lua_setglobal(lua, "inherit");

	lua_pushcfunction(lua, &UUID);
	lua_setglobal(lua, "uuid");

	lua_newtable(lua);
	lua_pushcfunction(lua, &B64Encode);
	lua_setfield(lua, -2, "encode");
	lua_pushcfunction(lua, &B64Decode);
	lua_setfield(lua, -2, "decode");
	lua_setglobal(lua, "b64");
}